suppressPackageStartupMessages(library("ggplot2"))
theme_set(ggpubr::theme_pubr(base_size=10, legend='bottom'))
suppressPackageStartupMessages(library("DESeq2"))
#Note that we do not have Clark Scores or MIA scores for AVAST-M lymph node samples and these need to be commented out to avoid errors.
#Load the data
tissue<-c("Skin")
load(paste("~/Desktop/Melanoma/deseq2Results/tc",tissue,"EventMetNo_VS_tc",tissue,"EventMetYes_CovariateCorrection.deseq2/de.Rdata", sep=""))
#find ENSEMBL ID corresponding to GRAMD1B
geneName<-"GRAMD1B"
select<-as.character(res.annot$id[res.annot$Name==geneName])
#extract expression of this gene
expressionData <- data.frame(assay(vsd)[select, ])
#Replace ENSEMBL IDs with corresponding gene names
names(expressionData) <- geneName
stand.fun <- function(x){(x-mean(x,na.rm=TRUE))/sd(x,na.rm=TRUE)}
stdSignature<-stand.fun(expressionData$GRAMD1B)
names(stdSignature)<-rownames(expressionData) #make sure they retain the name of the samples
#Multiply by beta coefficient?
#betaCoeff <- read.table("~/Desktop/Melanoma/betaCoeff.tsv", sep = "\t", header = TRUE, quote = "")
#extract beta coefficient for the gene of interest
#betaCoeffGene <- betaCoeff[select, "EventMet_Yes_vs_No"]
#weightedSignature <- expressionData*betaCoeffGene
#standardization of the weighted signature
#stand.fun <- function(x){(x-mean(x,na.rm=TRUE))/sd(x,na.rm=TRUE)}
#stdWeightedSignature <- stand.fun(weightedSignature$GRAMD1B)
#names(stdWeightedSignature)<-rownames(weightedSignature) #make sure they retain the name of the samples
As expected the absolute values after standardization of weighted signature (stand.fun(weightedSignature$GRAMD1B)) is same as those of standardized expression values (stand.fun(expressionData$GRAMD1B)). The only difference comes from sign of the beta coefficient which is negative for GRAMD1B
#Load clinical data
clinicalData<-data.frame(colData(vsd))
clinicalData$Signature<- stdSignature[rownames(clinicalData)] #merge signature in clinical data frame
#Check association of GRAMD1B with relapse
my_comparisons <- list( c("Yes", "No"))
ggplot(clinicalData[!is.na(clinicalData$EventMet),], aes(x=EventMet, y=Signature))+
geom_violin()+
geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
#scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
xlab("Distant metastases (AVAST-M Skin)")+
ylab("VST normalized GRAMD1B expression (stand.)")+
#geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
theme(text=element_text(size=7, family="sans"))+
ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

#ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+
Find suitable cut-off. After talking to Roy, it seems that we could go for a weighted GRAMD1B value of 0? Let’s check the median value. May be I can take the median cut-off and perform chi-squared test to check if there is no difference between the mean of 2 groups?
#Divide in high/low groups based on mean and median
co_mean<-mean(clinicalData$Signature)
co_median<-median(clinicalData$Signature)
co_0_33<-quantile(clinicalData$Signature, 0.33)
co_0_66<-quantile(clinicalData$Signature, 0.66)
if (tissue=="Skin") {
co_density_intersection<--0.223156179#0.235202939
}else{
co_density_intersection<-0.3481057115#-0.223156179#0.235202939
}
suppressPackageStartupMessages(library(plotly))
p<-ggplot(data = clinicalData, aes(x = Signature, fill=EventMet)) +
geom_density(alpha=0.3)+
geom_vline(xintercept=c(co_mean, co_median, co_0_33, co_0_66, co_density_intersection), linetype=c('twodash', 'longdash', 'dotted', 'dotdash', 'dashed'))+
ggtitle(tissue)
ggplotly(p)
clinicalData$SignatureGroupMean <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_mean), "High", "Low"))
clinicalData$SignatureGroupMedian <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_median), "High", "Low"))
clinicalData$SignatureGroup0_33 <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_0_33), "High", "Low"))
clinicalData$SignatureGroup0_66 <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_0_66), "High", "Low"))
clinicalData$SignatureGroupDensityIntersection <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_density_intersection), "High", "Low"))
print(chisq.test(clinicalData$SignatureGroupMean, clinicalData$EventMet))
Pearson's Chi-squared test with Yates' continuity correction
data: clinicalData$SignatureGroupMean and clinicalData$EventMet
X-squared = 9.0843, df = 1, p-value = 0.002578
print(chisq.test(clinicalData$SignatureGroupMedian, clinicalData$EventMet))
Pearson's Chi-squared test with Yates' continuity correction
data: clinicalData$SignatureGroupMedian and clinicalData$EventMet
X-squared = 8.3039, df = 1, p-value = 0.003956
print(chisq.test(clinicalData$SignatureGroup0_33, clinicalData$EventMet))
Pearson's Chi-squared test with Yates' continuity correction
data: clinicalData$SignatureGroup0_33 and clinicalData$EventMet
X-squared = 13.838, df = 1, p-value = 0.0001992
print(chisq.test(clinicalData$SignatureGroup0_66, clinicalData$EventMet))
Pearson's Chi-squared test with Yates' continuity correction
data: clinicalData$SignatureGroup0_66 and clinicalData$EventMet
X-squared = 8.8431, df = 1, p-value = 0.002942
print(chisq.test(clinicalData$SignatureGroupDensityIntersection, clinicalData$EventMet))
Pearson's Chi-squared test with Yates' continuity correction
data: clinicalData$SignatureGroupDensityIntersection and clinicalData$EventMet
X-squared = 9.852, df = 1, p-value = 0.001696
#Check association of NRAS with other clinical covariates
print(chisq.test(clinicalData$NRAS, clinicalData$EventMet))
Pearson's Chi-squared test with Yates' continuity correction
data: clinicalData$NRAS and clinicalData$EventMet
X-squared = 2.5747, df = 1, p-value = 0.1086
print(chisq.test(clinicalData$NRAS, clinicalData$Bres))
Pearson's Chi-squared test
data: clinicalData$NRAS and clinicalData$Bres
X-squared = 3.0461, df = 2, p-value = 0.2181
print(chisq.test(clinicalData$NRAS, clinicalData$Ulc))
Pearson's Chi-squared test with Yates' continuity correction
data: clinicalData$NRAS and clinicalData$Ulc
X-squared = 0.16538, df = 1, p-value = 0.6843
#Check association of GRAMD1B with ulceration
my_comparisons <- list( c("Present", "Absent"))
ggplot(clinicalData[!is.na(clinicalData$Ulc),], aes(x=Ulc, y=Signature))+
geom_violin()+
geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
#scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
xlab("Ulc (AVAST-M Skin)")+
ylab("VST normalized GRAMD1B expression (stand.)")+
#geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
theme(text=element_text(size=7, family="sans"))+
ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

#ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+
#Check association of GRAMD1B with Bres
my_comparisons <- list( c("<= 2.0 mm", ">2-4mm"), c("<= 2.0 mm", ">4.0mm"), c(">2-4mm", ">4.0mm"))
ggplot(clinicalData[!is.na(clinicalData$Bres),], aes(x=Bres, y=Signature))+
geom_violin()+
geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
#scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
xlab("Bres (AVAST-M Skin)")+
ylab("VST normalized GRAMD1B expression (stand.)")+
#geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
theme(text=element_text(size=7, family="sans"))+
ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+

#Check association of GRAMD1B with Stage
my_comparisons <- list( c("IIB", "IIC"), c("IIB", "IIIA"), c("IIB", "IIIB"), c("IIB", "IIIC"),
c("IIC", "IIIA"), c("IIC", "IIIB"), c("IIC", "IIIC"),
c("IIIA", "IIIB"), c("IIIA", "IIIC"),
c("IIIB", "IIIC"))
ggplot(clinicalData[!is.na(clinicalData$Stage),], aes(x=Stage, y=Signature))+
geom_violin()+
geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
#scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
xlab("Stage (AVAST-M Skin)")+
ylab("VST normalized GRAMD1B expression (stand.)")+
#geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
theme(text=element_text(size=7, family="sans"))+
ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+

ggplot(clinicalData[!is.na(clinicalData$Stage_binary),], aes(x=Stage_binary, y=Signature))+
geom_violin()+
geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
#scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
xlab("Stage (AVAST-M Skin)")+
ylab("VST normalized GRAMD1B expression (stand.)")+
#geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
theme(text=element_text(size=7, family="sans"))+
ggpubr::stat_compare_means(method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

#ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")
#Check association of GRAMD1B with Site
my_comparisons <- list( c("Head and neck", "Lower lims"), c("Head and neck", "Trunk"), c("Head and neck", "Upper limbs"),
c("Lower lims", "Trunk"), c("Lower lims", "Upper limbs"),
c("Trunk", "Upper limbs"))
ggplot(clinicalData[!is.na(clinicalData$Site),], aes(x=Site, y=Signature))+
geom_violin()+
geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
#scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
xlab("Site (AVAST-M Skin)")+
ylab("VST normalized GRAMD1B expression (stand.)")+
#geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
theme(text=element_text(size=7, family="sans"))+
ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+

#Check association of GRAMD1B with BRAF mutation
my_comparisons <- list( c("V600E", "WT"))
ggplot(clinicalData[!is.na(clinicalData$BRAF),], aes(x=BRAF, y=Signature))+
geom_violin()+
geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
#scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
xlab("BRAF (AVAST-M Skin)")+
ylab("VST normalized GRAMD1B expression (stand.)")+
#geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
theme(text=element_text(size=7, family="sans"))+
ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

#ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+
#Check association of GRAMD1B with NRAS mutation
my_comparisons <- list( c("Mutant", "WT"))
ggplot(clinicalData[!is.na(clinicalData$NRAS),], aes(x=NRAS, y=Signature))+
geom_violin()+
geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
#scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
xlab("NRAS (AVAST-M Skin)")+
ylab("VST normalized GRAMD1B expression (stand.)")+
#geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
theme(text=element_text(size=7, family="sans"))+
ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

#ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+
#Check association of GRAMD1B with TIL counts
jt_ns_sm_scores = read.table("../../../JT_NS_SM_Combined.xls - JT_NS_SM.tsv", sep = "\t", header = TRUE, quote = "")
combinedScore <- data.frame("Scanned_file.me"= jt_ns_sm_scores$JT_Scanned_file.me,
"R.Seq_sampleID"= jt_ns_sm_scores$JT_R.Seq_sampleID,
"ClarkScore"= ifelse(jt_ns_sm_scores$JT_Clark_score==jt_ns_sm_scores$NS_Clark_score,
jt_ns_sm_scores$JT_Clark_score,
jt_ns_sm_scores$SM_Clark_score),
"MIAScore"= ifelse(jt_ns_sm_scores$JT_TIL_grade==jt_ns_sm_scores$NS_TIL.GRADE,
jt_ns_sm_scores$JT_TIL_grade,
jt_ns_sm_scores$SM_TIL_grade))
#Remove duplicated entries
combinedScore<-combinedScore[!duplicated(combinedScore$R.Seq_sampleID), ]
rownames(combinedScore)<-combinedScore$R.Seq_sampleID #rename rownames with sample ids
rownames(clinicalData)<-clinicalData$RNA.Seq.Sample
df<-data.frame("EventMet"=clinicalData$EventMet,
"ClarkScore"=combinedScore[rownames(clinicalData), "ClarkScore"],
"MIAScore"=combinedScore[rownames(clinicalData), "MIAScore"],
"Signature"=clinicalData$Signature)
rownames(df)<-rownames(clinicalData)
df$ClarkScore <- as.factor(df$ClarkScore)
levels(df$ClarkScore) <- c("absent", "nonbrisk", "brisk")
my_comparisons <- list( c("absent", "nonbrisk"), c("nonbrisk", "brisk"), c("absent", "brisk") )
#df$JT_Clark_score <- factor(df$JT_Clark_score, levels = c("absent", "nonbrisk", "brisk"))
ggplot(df[!is.na(df$ClarkScore), ], aes(x=ClarkScore, y=Signature#, color=DRBrain
))+
geom_violin()+
geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
#scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
xlab("Clark score (AVAST-M Skin)")+
ylab("VST normalized GRAMD1B expression (stand.)")+
#geom_hline(yintercept = quantile(df$Signature, 0.75), color = "grey", linetype = "longdash")+
theme(text=element_text(size=7, family="sans"))+
ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+

# facet_wrap(~DRBrain)# Add global p-value
my_comparisons <- list( c("0", "1"), c("0", "2"), c("0", "3"), c("1", "2"), c("1", "3"), c("2", "3"))
df$MIAScore <- as.factor(df$MIAScore)
ggplot(df[!is.na(df$MIAScore), ], aes(x=MIAScore, y=Signature#, color=DRBrain
))+
geom_violin()+
geom_jitter(height = 0, width = 0.1, alpha = 0.5)+
scale_fill_brewer(type="qual", palette = "Dark2", name = "MIA score")+
xlab("MIA score (AVAST-M Skin)")+
ylab("VST normalized GRAMD1B expression (stand.)")+
#geom_hline(yintercept = quantile(df$Signature, 0.75), color = "grey", linetype = "longdash")+
theme(text=element_text(size=7, family="sans"))+
ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+

#facet_wrap(~DRBrain)
# Add global p-value
#my_comparisons <- list( c("Mutant", "WT"))
ggplot(clinicalData[!is.na(clinicalData$treatment),], aes(x=treatment, y=Signature))+
geom_violin()+
geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
#scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
xlab("Treatment (AVAST-M Skin)")+
ylab("VST normalized GRAMD1B expression (stand.)")+
#geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
theme(text=element_text(size=7, family="sans"))+
ggpubr::stat_compare_means(method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

#ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+
#my_comparisons <- list( c("Mutant", "WT"))
ggplot(clinicalData[!is.na(clinicalData$ECOG),], aes(x=ECOG, y=Signature))+
geom_violin()+
geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
#scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
xlab("ECOG (AVAST-M Skin)")+
ylab("VST normalized GRAMD1B expression (stand.)")+
#geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
theme(text=element_text(size=7, family="sans"))+
ggpubr::stat_compare_means(method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

#ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+
#Perform differential expression analysis
combinedScore$ClarkScore <- as.factor(combinedScore$ClarkScore)
levels(combinedScore$ClarkScore) <- c("absent", "nonbrisk", "brisk")
clinicalData$ClarkScore<-combinedScore[rownames(clinicalData), "ClarkScore"]
#merge(clinicalData, combinedScore, by.x="RNA.Seq.Sample", by.y="R.Seq_sampleID")
#rownames(clinicalData)<-clinicalData$RNA.Seq.Sample
#clinicalData$ClarkScore=combinedScore$ClarkScore[rownames(clinicalData)]
#While accounting for Stage, NRAS mutation, TIL score and EventMet
#Subset to only those samples which have non-missing entires for these covariates
clinicalData_sub<-clinicalData[(!is.na(clinicalData$Stage))&(!is.na(clinicalData$NRAS))&(!is.na(clinicalData$ClarkScore))&(!is.na(clinicalData$EventMet)), ]
data.f_sub<-data.f[, rownames(clinicalData_sub)]
table(clinicalData_sub$SignatureGroupMean)
High Low
34 32
dds1 <- DESeqDataSetFromMatrix(countData = data.f_sub,
colData = clinicalData_sub,
design = ~ Stage+NRAS+ClarkScore+EventMet+SignatureGroupMean)
totalcounts1.gene = rowSums(counts(dds1))
dds1 <- dds1[totalcounts1.gene>=10,]
# dimensions post filtering
m = nrow(dds1)
n = ncol(dds1)
dds1 <- DESeq(dds1)
using pre-existing size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
11 rows did not converge in beta, labelled in mcols(object)$betaConv. Use larger maxit argument with nbinomWaldTest
res1 <- results(dds1)
res1
log2 fold change (MLE): SignatureGroupMean Low vs High
Wald test p-value: SignatureGroupMean Low vs High
DataFrame with 37079 rows and 6 columns
baseMean log2FoldChange lfcSE stat pvalue padj
<numeric> <numeric> <numeric> <numeric> <numeric> <numeric>
ENSG00000000003 861.278 0.11366862 0.1629208 0.69769243 0.48536957 0.8133089
ENSG00000000005 33.432 -0.69252919 0.4546602 -1.52317957 0.12771379 0.4931014
ENSG00000000419 5709.545 -0.00041328 0.1010882 -0.00408831 0.99673801 0.9993295
ENSG00000000457 1391.790 0.16335820 0.0957011 1.70696327 0.08782888 0.4188545
ENSG00000000460 1956.996 0.39953052 0.1421792 2.81004990 0.00495338 0.0840005
... ... ... ... ... ... ...
ENSG00000284738 6.408754 0.272156 0.431137 0.631254 0.5278747 0.835093
ENSG00000284740 6.870408 0.591347 0.350720 1.686092 0.0917782 0.425316
ENSG00000284744 22.946725 -0.591098 0.374356 -1.578974 0.1143421 0.470308
ENSG00000284747 6.877460 -0.574416 0.327618 -1.753310 0.0795488 0.399437
ENSG00000284748 0.235384 -0.320125 1.496303 -0.213944 0.8305906 NA
#Merge with gene names and check if the differentially expressed genes make sense
res<-data.frame(res1)
res<-res[order(res$padj), ]
res$Name<-res.annot[rownames(res), "Name"]
head(res)
#GRAMD1B is differentially expressed in GRAMD1B groups and is downregulated in the low expression group compared to the high-expression group so atleast this makes sense.
#Perform lfc shrinkage for GSEA
library("apeglm")
coefName = resultsNames(dds1)
resWithLfcShrinkage = data.frame(lfcShrink(dds1, coef = tail(coefName, n=1), type="apeglm"))
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
sequence count data: removing the noise and preserving large differences.
Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
#Add suffix to colnames for easy identification later on.
colnames(resWithLfcShrinkage) = paste(colnames(resWithLfcShrinkage),"lfcShrinkApplied", sep = "_")
#Add ENSEMBL ID as a column in the data frame to ease merging later
resWithLfcShrinkage$id = rownames(resWithLfcShrinkage)
#Merge these with DE original DE results
res$id<-rownames(res)
deResultsUpdated = merge(x = res, y = data.frame(resWithLfcShrinkage), by = "id")
deResultsUpdated = deResultsUpdated[order(deResultsUpdated$padj),]
#Save the results
write.table(deResultsUpdated[deResultsUpdated$padj_lfcShrinkApplied<0.1, ], file = "../results/de_gsea/deWithLfcSkrinkageApplied_GRAMD1B_all.tsv", col.names = TRUE, row.names = FALSE, quote = FALSE, sep = '\t')
#Save the differentially expressed genes (padj<0.1)
write.table(deResultsUpdated[deResultsUpdated$padj_lfcShrinkApplied<0.1, ], file = "../results/de_gsea/deWithLfcSkrinkageApplied_GRAMD1B_padj_0_1.tsv", col.names = TRUE, row.names = FALSE, quote = FALSE, sep = '\t')
head(deResultsUpdated)
#Create a ranked list for GSEA
#Create ranked list for running Pre-ranked GSEA tool
rankedList = na.omit(data.frame(deResultsUpdated$Name, deResultsUpdated$log2FoldChange_lfcShrinkApplied))
rankedList = rankedList[with(rankedList, order(deResultsUpdated.log2FoldChange_lfcShrinkApplied)), ]
#Save the ranked list to a new file for GSEA.
write.table(rankedList, file = "../results/de_gsea/rankedList_GRAMD1B.rnk", col.names = FALSE, row.names = FALSE, quote = FALSE, sep = '\t')
#Plot survival curves between the continuous signature and the two groups ## First add necesssary columns
suppressPackageStartupMessages(library("survival"))
# survival outcome 1: "d" for death and "ltrc" for left truncated and right censored
clinicalData$survival_d_ltrc = Surv(time = as.numeric(difftime(clinicalData$DOE, clinicalData$DDiag))/365.25,
time2 = as.numeric(difftime(clinicalData$DOC, clinicalData$DDiag))/365.25,
event = ifelse(clinicalData$Dead == FALSE, 0, 1))
# survival outcome 2: "rd" for relapse or death, "ltrc" for left truncated and right censored
clinicalData$survival_rd_ltrc = Surv(time = as.numeric(difftime(clinicalData$DOE, clinicalData$DDiag))/365.25,
time2 = as.numeric(difftime(apply(clinicalData[, c("DOC", "DDistMets")],1,min,na.rm=TRUE), clinicalData$DDiag))/365.25,
event = ifelse((clinicalData$Dead == FALSE & clinicalData$EventMet == "No"), 0, 1))
if(tissue=="Skin"){
jt_ns_sm_scores = read.table("../JT_NS_SM_Combined.xls - JT_NS_SM.tsv", sep = "\t", header = TRUE, quote = "")
combinedScore <- data.frame("Scanned_file.me"= jt_ns_sm_scores$JT_Scanned_file.me,
"R.Seq_sampleID"= jt_ns_sm_scores$JT_R.Seq_sampleID,
"ClarkScore"=ifelse(jt_ns_sm_scores$JT_Clark_score==jt_ns_sm_scores$NS_Clark_score,
jt_ns_sm_scores$JT_Clark_score,
jt_ns_sm_scores$SM_Clark_score),
"MIAScore"= ifelse(jt_ns_sm_scores$JT_TIL_grade==jt_ns_sm_scores$NS_TIL.GRADE,
jt_ns_sm_scores$JT_TIL_grade,
jt_ns_sm_scores$SM_TIL_grade))
#Remove duplicated entries
combinedScore<-combinedScore[!duplicated(combinedScore$R.Seq_sampleID), ]
rownames(combinedScore)<-combinedScore$R.Seq_sampleID #rename rownames with sample ids
combinedScore$ClarkScore <- as.factor(combinedScore$ClarkScore)
levels(combinedScore$ClarkScore) <- c("absent", "nonbrisk", "brisk")
clinicalData<-merge(clinicalData, combinedScore, by.x="RNA.Seq.Sample", by.y="R.Seq_sampleID")
rownames(clinicalData)<-clinicalData$RNA.Seq.Sample
}
then calculate the values
os<-data.frame()
pfs<-data.frame()
temp<-data.frame()
combinations<-c("Signature", "SignatureGroupMean", "SignatureGroupMedian", "SignatureGroup0_33", "SignatureGroup0_66", "SignatureGroupDensityIntersection")
for (combination in combinations) {
if (tissue=="Skin") {
os_formula<-paste0("survival_d_ltrc~",combination,"+Sex+Age+as.numeric(Nclass)+as.character(Stage)+ECOG+treatment+ClarkScore")
pfs_formula<-paste0("survival_rd_ltrc~",combination,"+Sex+Age+as.numeric(Nclass)+as.character(Stage)+ECOG+treatment+ClarkScore")
} else{
os_formula<-paste0("survival_d_ltrc~",combination,"+Sex+Age+as.numeric(Nclass)+as.character(Stage)+ECOG+treatment")
pfs_formula<-paste0("survival_rd_ltrc~",combination,"+Sex+Age+as.numeric(Nclass)+as.character(Stage)+ECOG+treatment")
}
fit = coef(summary(coxph(as.formula(os_formula), data=clinicalData)))
mid = fit[1,c("exp(coef)")]
low = exp(fit[1,c("coef")]-
qnorm(.975)*fit[1,c("se(coef)")])
high = exp(fit[1,c("coef")]+
qnorm(.975)*fit[1,c("se(coef)")])
pval = fit[1,c("Pr(>|z|)")]
temp["Signature",c("Signature", "HR","low","high","pval")] = c(rownames(fit)[1],mid,low,high,pval)
os<-rbind(os, temp)
fit = coef(summary(coxph(as.formula(pfs_formula), data=clinicalData)))
mid = fit[1,c("exp(coef)")]
low = exp(fit[1,c("coef")]-
qnorm(.975)*fit[1,c("se(coef)")])
high = exp(fit[1,c("coef")]+
qnorm(.975)*fit[1,c("se(coef)")])
pval = fit[1,c("Pr(>|z|)")]
temp["Signature",c("Signature", "HR","low","high","pval")] = c(rownames(fit)[1],mid,low,high,pval)
pfs<-rbind(pfs, temp)
}
write.table(os, paste("../results/survival/AVAST-M_",tissue,"_os.tsv", sep=""), sep="\t", col.names = T, row.names = F)
write.table(pfs, paste("../results/survival/AVAST-M_",tissue,"_pfs.tsv", sep=""), sep="\t", col.names = T, row.names = F)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KCJnZ3Bsb3QyIikpCnRoZW1lX3NldChnZ3B1YnI6OnRoZW1lX3B1YnIoYmFzZV9zaXplPTEwLCBsZWdlbmQ9J2JvdHRvbScpKQpgYGAKCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgiREVTZXEyIikpCmBgYAoKI05vdGUgdGhhdCB3ZSBkbyBub3QgaGF2ZSBDbGFyayBTY29yZXMgb3IgTUlBIHNjb3JlcyBmb3IgQVZBU1QtTSBseW1waCBub2RlIHNhbXBsZXMgYW5kIHRoZXNlIG5lZWQgdG8gYmUgY29tbWVudGVkIG91dCB0byBhdm9pZCBlcnJvcnMuIApgYGB7cn0KI0xvYWQgdGhlIGRhdGEKdGlzc3VlPC1jKCJTa2luIikKbG9hZChwYXN0ZSgifi9EZXNrdG9wL01lbGFub21hL2Rlc2VxMlJlc3VsdHMvdGMiLHRpc3N1ZSwiRXZlbnRNZXROb19WU190YyIsdGlzc3VlLCJFdmVudE1ldFllc19Db3ZhcmlhdGVDb3JyZWN0aW9uLmRlc2VxMi9kZS5SZGF0YSIsIHNlcD0iIikpCmBgYAoKYGBge3J9CiNmaW5kIEVOU0VNQkwgSUQgY29ycmVzcG9uZGluZyB0byBHUkFNRDFCCmdlbmVOYW1lPC0iR1JBTUQxQiIKc2VsZWN0PC1hcy5jaGFyYWN0ZXIocmVzLmFubm90JGlkW3Jlcy5hbm5vdCROYW1lPT1nZW5lTmFtZV0pCiNleHRyYWN0IGV4cHJlc3Npb24gb2YgdGhpcyBnZW5lCmV4cHJlc3Npb25EYXRhIDwtIGRhdGEuZnJhbWUoYXNzYXkodnNkKVtzZWxlY3QsIF0pCiNSZXBsYWNlIEVOU0VNQkwgSURzIHdpdGggY29ycmVzcG9uZGluZyBnZW5lIG5hbWVzCm5hbWVzKGV4cHJlc3Npb25EYXRhKSA8LSBnZW5lTmFtZQpzdGFuZC5mdW4gPC0gZnVuY3Rpb24oeCl7KHgtbWVhbih4LG5hLnJtPVRSVUUpKS9zZCh4LG5hLnJtPVRSVUUpfQpzdGRTaWduYXR1cmU8LXN0YW5kLmZ1bihleHByZXNzaW9uRGF0YSRHUkFNRDFCKQpuYW1lcyhzdGRTaWduYXR1cmUpPC1yb3duYW1lcyhleHByZXNzaW9uRGF0YSkgI21ha2Ugc3VyZSB0aGV5IHJldGFpbiB0aGUgbmFtZSBvZiB0aGUgc2FtcGxlcwpgYGAKCmBgYHtyfQojTXVsdGlwbHkgYnkgYmV0YSBjb2VmZmljaWVudD8KI2JldGFDb2VmZiA8LSByZWFkLnRhYmxlKCJ+L0Rlc2t0b3AvTWVsYW5vbWEvYmV0YUNvZWZmLnRzdiIsIHNlcCA9ICJcdCIsIGhlYWRlciA9IFRSVUUsIHF1b3RlID0gIiIpCiNleHRyYWN0IGJldGEgY29lZmZpY2llbnQgZm9yIHRoZSBnZW5lIG9mIGludGVyZXN0CiNiZXRhQ29lZmZHZW5lIDwtIGJldGFDb2VmZltzZWxlY3QsICJFdmVudE1ldF9ZZXNfdnNfTm8iXQojd2VpZ2h0ZWRTaWduYXR1cmUgPC0gZXhwcmVzc2lvbkRhdGEqYmV0YUNvZWZmR2VuZQojc3RhbmRhcmRpemF0aW9uIG9mIHRoZSB3ZWlnaHRlZCBzaWduYXR1cmUKI3N0YW5kLmZ1biA8LSBmdW5jdGlvbih4KXsoeC1tZWFuKHgsbmEucm09VFJVRSkpL3NkKHgsbmEucm09VFJVRSl9CiNzdGRXZWlnaHRlZFNpZ25hdHVyZSA8LSBzdGFuZC5mdW4od2VpZ2h0ZWRTaWduYXR1cmUkR1JBTUQxQikKI25hbWVzKHN0ZFdlaWdodGVkU2lnbmF0dXJlKTwtcm93bmFtZXMod2VpZ2h0ZWRTaWduYXR1cmUpICNtYWtlIHN1cmUgdGhleSByZXRhaW4gdGhlIG5hbWUgb2YgdGhlIHNhbXBsZXMKYGBgCgpBcyBleHBlY3RlZCB0aGUgYWJzb2x1dGUgdmFsdWVzIGFmdGVyIHN0YW5kYXJkaXphdGlvbiBvZiB3ZWlnaHRlZCBzaWduYXR1cmUgKHN0YW5kLmZ1bih3ZWlnaHRlZFNpZ25hdHVyZVwkR1JBTUQxQikpIGlzIHNhbWUgYXMgdGhvc2Ugb2Ygc3RhbmRhcmRpemVkIGV4cHJlc3Npb24gdmFsdWVzIChzdGFuZC5mdW4oZXhwcmVzc2lvbkRhdGFcJEdSQU1EMUIpKS4gVGhlIG9ubHkgZGlmZmVyZW5jZSBjb21lcyBmcm9tIHNpZ24gb2YgdGhlIGJldGEgY29lZmZpY2llbnQgd2hpY2ggaXMgbmVnYXRpdmUgZm9yIEdSQU1EMUIKCmBgYHtyfQojTG9hZCBjbGluaWNhbCBkYXRhCmNsaW5pY2FsRGF0YTwtZGF0YS5mcmFtZShjb2xEYXRhKHZzZCkpCmNsaW5pY2FsRGF0YSRTaWduYXR1cmU8LSBzdGRTaWduYXR1cmVbcm93bmFtZXMoY2xpbmljYWxEYXRhKV0gI21lcmdlIHNpZ25hdHVyZSBpbiBjbGluaWNhbCBkYXRhIGZyYW1lCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCByZWxhcHNlCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJZZXMiLCAiTm8iKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJEV2ZW50TWV0KSxdLCBhZXMoeD1FdmVudE1ldCwgeT1TaWduYXR1cmUpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiRGlzdGFudCBtZXRhc3Rhc2VzIChBVkFTVC1NIFNraW4pIikrCiAgeWxhYigiVlNUIG5vcm1hbGl6ZWQgR1JBTUQxQiBleHByZXNzaW9uIChzdGFuZC4pIikrCiAgI2dlb21faGxpbmUoeWludGVyY2VwdCA9IHF1YW50aWxlKGNsaW5pY2FsRGF0YSRTaWduYXR1cmUsIDAuNzUpLCBjb2xvciA9ICJncmV5IiwgbGluZXR5cGUgPSAibG9uZ2Rhc2giKSsKICB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTcsICBmYW1pbHk9InNhbnMiKSkrCiAgZ2dwdWJyOjpzdGF0X2NvbXBhcmVfbWVhbnMoY29tcGFyaXNvbnMgPSBteV9jb21wYXJpc29ucywgbWV0aG9kID0gInQudGVzdCIsIHNpemUgPSAyLjUsIGZhbWlseT0ic2FucyIpIysgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogICNnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhsYWJlbC55ID0gNiwgbWV0aG9kID0gImFub3ZhIiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKwoKYGBgCkZpbmQgc3VpdGFibGUgY3V0LW9mZi4gQWZ0ZXIgdGFsa2luZyB0byBSb3ksIGl0IHNlZW1zIHRoYXQgd2UgY291bGQgZ28gZm9yIGEgd2VpZ2h0ZWQgR1JBTUQxQiB2YWx1ZSBvZiAwPwpMZXQncyBjaGVjayB0aGUgbWVkaWFuIHZhbHVlLiBNYXkgYmUgSSBjYW4gdGFrZSB0aGUgbWVkaWFuIGN1dC1vZmYgYW5kIHBlcmZvcm0gY2hpLXNxdWFyZWQgdGVzdCB0byBjaGVjayBpZiB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG1lYW4gb2YgMiBncm91cHM/CmBgYHtyfQojRGl2aWRlIGluIGhpZ2gvbG93IGdyb3VwcyBiYXNlZCBvbiBtZWFuIGFuZCBtZWRpYW4KY29fbWVhbjwtbWVhbihjbGluaWNhbERhdGEkU2lnbmF0dXJlKQpjb19tZWRpYW48LW1lZGlhbihjbGluaWNhbERhdGEkU2lnbmF0dXJlKQpjb18wXzMzPC1xdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjMzKQpjb18wXzY2PC1xdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjY2KQppZiAodGlzc3VlPT0iU2tpbiIpIHsKICBjb19kZW5zaXR5X2ludGVyc2VjdGlvbjwtLTAuMjIzMTU2MTc5IzAuMjM1MjAyOTM5Cn1lbHNlewogIGNvX2RlbnNpdHlfaW50ZXJzZWN0aW9uPC0wLjM0ODEwNTcxMTUjLTAuMjIzMTU2MTc5IzAuMjM1MjAyOTM5Cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkocGxvdGx5KSkKcDwtZ2dwbG90KGRhdGEgPSBjbGluaWNhbERhdGEsIGFlcyh4ID0gU2lnbmF0dXJlLCBmaWxsPUV2ZW50TWV0KSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjMpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKGNvX21lYW4sIGNvX21lZGlhbiwgY29fMF8zMywgY29fMF82NiwgY29fZGVuc2l0eV9pbnRlcnNlY3Rpb24pLCBsaW5ldHlwZT1jKCd0d29kYXNoJywgJ2xvbmdkYXNoJywgJ2RvdHRlZCcsICdkb3RkYXNoJywgJ2Rhc2hlZCcpKSsKICBnZ3RpdGxlKHRpc3N1ZSkKZ2dwbG90bHkocCkgCmBgYAoKYGBge3J9CmNsaW5pY2FsRGF0YSRTaWduYXR1cmVHcm91cE1lYW4gPC0gYXMuZmFjdG9yKGlmZWxzZShjbGluaWNhbERhdGEkU2lnbmF0dXJlID49IGFzLm51bWVyaWMoY29fbWVhbiksICJIaWdoIiwgIkxvdyIpKQpjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXBNZWRpYW4gPC0gYXMuZmFjdG9yKGlmZWxzZShjbGluaWNhbERhdGEkU2lnbmF0dXJlID49IGFzLm51bWVyaWMoY29fbWVkaWFuKSwgIkhpZ2giLCAiTG93IikpCmNsaW5pY2FsRGF0YSRTaWduYXR1cmVHcm91cDBfMzMgPC0gYXMuZmFjdG9yKGlmZWxzZShjbGluaWNhbERhdGEkU2lnbmF0dXJlID49IGFzLm51bWVyaWMoY29fMF8zMyksICJIaWdoIiwgIkxvdyIpKQpjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXAwXzY2IDwtIGFzLmZhY3RvcihpZmVsc2UoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSA+PSBhcy5udW1lcmljKGNvXzBfNjYpLCAiSGlnaCIsICJMb3ciKSkKY2xpbmljYWxEYXRhJFNpZ25hdHVyZUdyb3VwRGVuc2l0eUludGVyc2VjdGlvbiA8LSBhcy5mYWN0b3IoaWZlbHNlKGNsaW5pY2FsRGF0YSRTaWduYXR1cmUgPj0gYXMubnVtZXJpYyhjb19kZW5zaXR5X2ludGVyc2VjdGlvbiksICJIaWdoIiwgIkxvdyIpKQoKcHJpbnQoY2hpc3EudGVzdChjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXBNZWFuLCBjbGluaWNhbERhdGEkRXZlbnRNZXQpKQpwcmludChjaGlzcS50ZXN0KGNsaW5pY2FsRGF0YSRTaWduYXR1cmVHcm91cE1lZGlhbiwgY2xpbmljYWxEYXRhJEV2ZW50TWV0KSkKcHJpbnQoY2hpc3EudGVzdChjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXAwXzMzLCBjbGluaWNhbERhdGEkRXZlbnRNZXQpKQpwcmludChjaGlzcS50ZXN0KGNsaW5pY2FsRGF0YSRTaWduYXR1cmVHcm91cDBfNjYsIGNsaW5pY2FsRGF0YSRFdmVudE1ldCkpCnByaW50KGNoaXNxLnRlc3QoY2xpbmljYWxEYXRhJFNpZ25hdHVyZUdyb3VwRGVuc2l0eUludGVyc2VjdGlvbiwgY2xpbmljYWxEYXRhJEV2ZW50TWV0KSkKYGBgCiNDaGVjayBhc3NvY2lhdGlvbiBvZiBOUkFTIHdpdGggb3RoZXIgY2xpbmljYWwgY292YXJpYXRlcwpgYGB7cn0KcHJpbnQoY2hpc3EudGVzdChjbGluaWNhbERhdGEkTlJBUywgY2xpbmljYWxEYXRhJEV2ZW50TWV0KSkKcHJpbnQoY2hpc3EudGVzdChjbGluaWNhbERhdGEkTlJBUywgY2xpbmljYWxEYXRhJEJyZXMpKQpwcmludChjaGlzcS50ZXN0KGNsaW5pY2FsRGF0YSROUkFTLCBjbGluaWNhbERhdGEkVWxjKSkKYGBgCiNDaGVjayBhc3NvY2lhdGlvbiBvZiBHUkFNRDFCIHdpdGggdWxjZXJhdGlvbgpgYGB7cn0KbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiUHJlc2VudCIsICJBYnNlbnQiKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJFVsYyksXSwgYWVzKHg9VWxjLCB5PVNpZ25hdHVyZSkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBhZXMoKSkrCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIkRhcmsyIiwgbmFtZSA9ICJDYW1fMTIxIHJpc2sgZ3JvdXAiKSsKICB4bGFiKCJVbGMgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLCBtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA2LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBCcmVzCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCI8PSAyLjAgbW0iLCAiPjItNG1tIiksIGMoIjw9IDIuMCBtbSIsICI+NC4wbW0iKSwgYygiPjItNG1tIiwgIj40LjBtbSIpKQpnZ3Bsb3QoY2xpbmljYWxEYXRhWyFpcy5uYShjbGluaWNhbERhdGEkQnJlcyksXSwgYWVzKHg9QnJlcywgeT1TaWduYXR1cmUpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiQnJlcyAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA2LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBTdGFnZQpgYGB7cn0KbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiSUlCIiwgIklJQyIpLCBjKCJJSUIiLCAiSUlJQSIpLCBjKCJJSUIiLCAiSUlJQiIpLCBjKCJJSUIiLCAiSUlJQyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjKCJJSUMiLCAiSUlJQSIpLCBjKCJJSUMiLCAiSUlJQiIpLCBjKCJJSUMiLCAiSUlJQyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjKCJJSUlBIiwgIklJSUIiKSwgYygiSUlJQSIsICJJSUlDIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGMoIklJSUIiLCAiSUlJQyIpKQpnZ3Bsb3QoY2xpbmljYWxEYXRhWyFpcy5uYShjbGluaWNhbERhdGEkU3RhZ2UpLF0sIGFlcyh4PVN0YWdlLCB5PVNpZ25hdHVyZSkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBhZXMoKSkrCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIkRhcmsyIiwgbmFtZSA9ICJDYW1fMTIxIHJpc2sgZ3JvdXAiKSsKICB4bGFiKCJTdGFnZSAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYApgYGB7cn0KZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJFN0YWdlX2JpbmFyeSksXSwgYWVzKHg9U3RhZ2VfYmluYXJ5LCB5PVNpZ25hdHVyZSkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBhZXMoKSkrCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIkRhcmsyIiwgbmFtZSA9ICJDYW1fMTIxIHJpc2sgZ3JvdXAiKSsKICB4bGFiKCJTdGFnZSAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrICMgQWRkIHBhaXJ3aXNlIGNvbXBhcmlzb25zIHAtdmFsdWUKICAjZ2dwdWJyOjpzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwueSA9IDksIG1ldGhvZCA9ICJhbm92YSIsIHNpemUgPSAyLjUsIGZhbWlseT0ic2FucyIpCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBTaXRlCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJIZWFkIGFuZCBuZWNrIiwgIkxvd2VyIGxpbXMiKSwgYygiSGVhZCBhbmQgbmVjayIsICJUcnVuayIpLCBjKCJIZWFkIGFuZCBuZWNrIiwgIlVwcGVyIGxpbWJzIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGMoIkxvd2VyIGxpbXMiLCAiVHJ1bmsiKSwgYygiTG93ZXIgbGltcyIsICJVcHBlciBsaW1icyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjKCJUcnVuayIsICJVcHBlciBsaW1icyIpKQpnZ3Bsb3QoY2xpbmljYWxEYXRhWyFpcy5uYShjbGluaWNhbERhdGEkU2l0ZSksXSwgYWVzKHg9U2l0ZSwgeT1TaWduYXR1cmUpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiU2l0ZSAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCgpgYGAKCiNDaGVjayBhc3NvY2lhdGlvbiBvZiBHUkFNRDFCIHdpdGggQlJBRiBtdXRhdGlvbgpgYGB7cn0KbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiVjYwMEUiLCAiV1QiKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJEJSQUYpLF0sIGFlcyh4PUJSQUYsIHk9U2lnbmF0dXJlKSkrCiAgZ2VvbV92aW9saW4oKSsKICBnZW9tX2ppdHRlcihoZWlnaHQgPSAwLCB3aWR0aCA9IDAuMSwgYWxwaGEgPSAwLjUsIGFlcygpKSsKICAjc2NhbGVfZmlsbF9icmV3ZXIodHlwZT0icXVhbCIsIHBhbGV0dGUgPSAiRGFyazIiLCBuYW1lID0gIkNhbV8xMjEgcmlzayBncm91cCIpKwogIHhsYWIoIkJSQUYgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLCBtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBOUkFTIG11dGF0aW9uCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJNdXRhbnQiLCAiV1QiKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJE5SQVMpLF0sIGFlcyh4PU5SQVMsIHk9U2lnbmF0dXJlKSkrCiAgZ2VvbV92aW9saW4oKSsKICBnZW9tX2ppdHRlcihoZWlnaHQgPSAwLCB3aWR0aCA9IDAuMSwgYWxwaGEgPSAwLjUsIGFlcygpKSsKICAjc2NhbGVfZmlsbF9icmV3ZXIodHlwZT0icXVhbCIsIHBhbGV0dGUgPSAiRGFyazIiLCBuYW1lID0gIkNhbV8xMjEgcmlzayBncm91cCIpKwogIHhsYWIoIk5SQVMgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLCBtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBUSUwgY291bnRzCmBgYHtyfQpqdF9uc19zbV9zY29yZXMgPSByZWFkLnRhYmxlKCIuLi8uLi8uLi9KVF9OU19TTV9Db21iaW5lZC54bHMgLSBKVF9OU19TTS50c3YiLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSBUUlVFLCBxdW90ZSA9ICIiKQpjb21iaW5lZFNjb3JlIDwtIGRhdGEuZnJhbWUoIlNjYW5uZWRfZmlsZS5tZSI9IGp0X25zX3NtX3Njb3JlcyRKVF9TY2FubmVkX2ZpbGUubWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSLlNlcV9zYW1wbGVJRCI9IGp0X25zX3NtX3Njb3JlcyRKVF9SLlNlcV9zYW1wbGVJRCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNsYXJrU2NvcmUiPSBpZmVsc2UoanRfbnNfc21fc2NvcmVzJEpUX0NsYXJrX3Njb3JlPT1qdF9uc19zbV9zY29yZXMkTlNfQ2xhcmtfc2NvcmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBqdF9uc19zbV9zY29yZXMkSlRfQ2xhcmtfc2NvcmUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAganRfbnNfc21fc2NvcmVzJFNNX0NsYXJrX3Njb3JlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1JQVNjb3JlIj0gaWZlbHNlKGp0X25zX3NtX3Njb3JlcyRKVF9USUxfZ3JhZGU9PWp0X25zX3NtX3Njb3JlcyROU19USUwuR1JBREUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBqdF9uc19zbV9zY29yZXMkSlRfVElMX2dyYWRlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGp0X25zX3NtX3Njb3JlcyRTTV9USUxfZ3JhZGUpKQojUmVtb3ZlIGR1cGxpY2F0ZWQgZW50cmllcwpjb21iaW5lZFNjb3JlPC1jb21iaW5lZFNjb3JlWyFkdXBsaWNhdGVkKGNvbWJpbmVkU2NvcmUkUi5TZXFfc2FtcGxlSUQpLCBdCnJvd25hbWVzKGNvbWJpbmVkU2NvcmUpPC1jb21iaW5lZFNjb3JlJFIuU2VxX3NhbXBsZUlEICNyZW5hbWUgcm93bmFtZXMgd2l0aCBzYW1wbGUgaWRzCmBgYAoKYGBge3J9CnJvd25hbWVzKGNsaW5pY2FsRGF0YSk8LWNsaW5pY2FsRGF0YSRSTkEuU2VxLlNhbXBsZQpkZjwtZGF0YS5mcmFtZSgiRXZlbnRNZXQiPWNsaW5pY2FsRGF0YSRFdmVudE1ldCwKICAgICAgICAgICAgICAgIkNsYXJrU2NvcmUiPWNvbWJpbmVkU2NvcmVbcm93bmFtZXMoY2xpbmljYWxEYXRhKSwgIkNsYXJrU2NvcmUiXSwKICAgICAgICAgICAgICAgIk1JQVNjb3JlIj1jb21iaW5lZFNjb3JlW3Jvd25hbWVzKGNsaW5pY2FsRGF0YSksICJNSUFTY29yZSJdLAogICAgICAgICAgICAgICAiU2lnbmF0dXJlIj1jbGluaWNhbERhdGEkU2lnbmF0dXJlKQpyb3duYW1lcyhkZik8LXJvd25hbWVzKGNsaW5pY2FsRGF0YSkKCmRmJENsYXJrU2NvcmUgPC0gYXMuZmFjdG9yKGRmJENsYXJrU2NvcmUpCmxldmVscyhkZiRDbGFya1Njb3JlKSA8LSBjKCJhYnNlbnQiLCAibm9uYnJpc2siLCAiYnJpc2siKQpgYGAKCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJhYnNlbnQiLCAibm9uYnJpc2siKSwgYygibm9uYnJpc2siLCAiYnJpc2siKSwgYygiYWJzZW50IiwgImJyaXNrIikgKQojZGYkSlRfQ2xhcmtfc2NvcmUgPC0gZmFjdG9yKGRmJEpUX0NsYXJrX3Njb3JlLCBsZXZlbHMgPSBjKCJhYnNlbnQiLCAibm9uYnJpc2siLCAiYnJpc2siKSkKZ2dwbG90KGRmWyFpcy5uYShkZiRDbGFya1Njb3JlKSwgXSwgYWVzKHg9Q2xhcmtTY29yZSwgeT1TaWduYXR1cmUjLCBjb2xvcj1EUkJyYWluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiQ2xhcmsgc2NvcmUgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoZGYkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA2LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCiAjIGZhY2V0X3dyYXAofkRSQnJhaW4pIyBBZGQgZ2xvYmFsIHAtdmFsdWUKYGBgCgpgYGB7cn0KbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiMCIsICIxIiksIGMoIjAiLCAiMiIpLCBjKCIwIiwgIjMiKSwgYygiMSIsICIyIiksIGMoIjEiLCAiMyIpLCBjKCIyIiwgIjMiKSkKZGYkTUlBU2NvcmUgPC0gYXMuZmFjdG9yKGRmJE1JQVNjb3JlKQpnZ3Bsb3QoZGZbIWlzLm5hKGRmJE1JQVNjb3JlKSwgXSwgYWVzKHg9TUlBU2NvcmUsIHk9U2lnbmF0dXJlIywgY29sb3I9RFJCcmFpbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41KSsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiTUlBIHNjb3JlIikrCiAgeGxhYigiTUlBIHNjb3JlIChBVkFTVC1NIFNraW4pIikrCiAgeWxhYigiVlNUIG5vcm1hbGl6ZWQgR1JBTUQxQiBleHByZXNzaW9uIChzdGFuZC4pIikrCiAgI2dlb21faGxpbmUoeWludGVyY2VwdCA9IHF1YW50aWxlKGRmJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLCBtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikrICMgQWRkIHBhaXJ3aXNlIGNvbXBhcmlzb25zIHAtdmFsdWUKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhsYWJlbC55ID0gNiwgbWV0aG9kID0gImFub3ZhIiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKwogICNmYWNldF93cmFwKH5EUkJyYWluKQogICAgICMgQWRkIGdsb2JhbCBwLXZhbHVlCgpgYGAKYGBge3J9CiNteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJNdXRhbnQiLCAiV1QiKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJHRyZWF0bWVudCksXSwgYWVzKHg9dHJlYXRtZW50LCB5PVNpZ25hdHVyZSkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBhZXMoKSkrCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIkRhcmsyIiwgbmFtZSA9ICJDYW1fMTIxIHJpc2sgZ3JvdXAiKSsKICB4bGFiKCJUcmVhdG1lbnQgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYApgYGB7cn0KI215X2NvbXBhcmlzb25zIDwtIGxpc3QoIGMoIk11dGFudCIsICJXVCIpKQpnZ3Bsb3QoY2xpbmljYWxEYXRhWyFpcy5uYShjbGluaWNhbERhdGEkRUNPRyksXSwgYWVzKHg9RUNPRywgeT1TaWduYXR1cmUpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiRUNPRyAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrICMgQWRkIHBhaXJ3aXNlIGNvbXBhcmlzb25zIHAtdmFsdWUKICAjZ2dwdWJyOjpzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwueSA9IDksIG1ldGhvZCA9ICJhbm92YSIsIHNpemUgPSAyLjUsIGZhbWlseT0ic2FucyIpIysKYGBgCgojUGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcwpgYGB7cn0KY29tYmluZWRTY29yZSRDbGFya1Njb3JlIDwtIGFzLmZhY3Rvcihjb21iaW5lZFNjb3JlJENsYXJrU2NvcmUpCmxldmVscyhjb21iaW5lZFNjb3JlJENsYXJrU2NvcmUpIDwtIGMoImFic2VudCIsICJub25icmlzayIsICJicmlzayIpCmNsaW5pY2FsRGF0YSRDbGFya1Njb3JlPC1jb21iaW5lZFNjb3JlW3Jvd25hbWVzKGNsaW5pY2FsRGF0YSksICJDbGFya1Njb3JlIl0KI21lcmdlKGNsaW5pY2FsRGF0YSwgY29tYmluZWRTY29yZSwgYnkueD0iUk5BLlNlcS5TYW1wbGUiLCBieS55PSJSLlNlcV9zYW1wbGVJRCIpCiNyb3duYW1lcyhjbGluaWNhbERhdGEpPC1jbGluaWNhbERhdGEkUk5BLlNlcS5TYW1wbGUKI2NsaW5pY2FsRGF0YSRDbGFya1Njb3JlPWNvbWJpbmVkU2NvcmUkQ2xhcmtTY29yZVtyb3duYW1lcyhjbGluaWNhbERhdGEpXQpgYGAKCmBgYHtyfQojV2hpbGUgYWNjb3VudGluZyBmb3IgU3RhZ2UsIE5SQVMgbXV0YXRpb24sIFRJTCBzY29yZSBhbmQgRXZlbnRNZXQKI1N1YnNldCB0byBvbmx5IHRob3NlIHNhbXBsZXMgd2hpY2ggaGF2ZSBub24tbWlzc2luZyBlbnRpcmVzIGZvciB0aGVzZSBjb3ZhcmlhdGVzCmNsaW5pY2FsRGF0YV9zdWI8LWNsaW5pY2FsRGF0YVsoIWlzLm5hKGNsaW5pY2FsRGF0YSRTdGFnZSkpJighaXMubmEoY2xpbmljYWxEYXRhJE5SQVMpKSYoIWlzLm5hKGNsaW5pY2FsRGF0YSRDbGFya1Njb3JlKSkmKCFpcy5uYShjbGluaWNhbERhdGEkRXZlbnRNZXQpKSwgXQpkYXRhLmZfc3ViPC1kYXRhLmZbLCByb3duYW1lcyhjbGluaWNhbERhdGFfc3ViKV0KYGBgCgpgYGB7cn0KdGFibGUoY2xpbmljYWxEYXRhX3N1YiRTaWduYXR1cmVHcm91cE1lYW4pCmBgYAoKYGBge3J9CmRkczEgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBkYXRhLmZfc3ViLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSAgID0gY2xpbmljYWxEYXRhX3N1YiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiAgICA9IH4gU3RhZ2UrTlJBUytDbGFya1Njb3JlK0V2ZW50TWV0K1NpZ25hdHVyZUdyb3VwTWVhbikKYGBgCgpgYGB7cn0KdG90YWxjb3VudHMxLmdlbmUgPSByb3dTdW1zKGNvdW50cyhkZHMxKSkKZGRzMSA8LSBkZHMxW3RvdGFsY291bnRzMS5nZW5lPj0xMCxdCmBgYAoKYGBge3J9CiMgZGltZW5zaW9ucyBwb3N0IGZpbHRlcmluZwptID0gbnJvdyhkZHMxKQpuID0gbmNvbChkZHMxKQpgYGAKCmBgYHtyfQpkZHMxIDwtIERFU2VxKGRkczEpCnJlczEgPC0gcmVzdWx0cyhkZHMxKQpyZXMxCmBgYApgYGB7cn0KI01lcmdlIHdpdGggZ2VuZSBuYW1lcyBhbmQgY2hlY2sgaWYgdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBtYWtlIHNlbnNlCnJlczwtZGF0YS5mcmFtZShyZXMxKQpyZXM8LXJlc1tvcmRlcihyZXMkcGFkaiksIF0KcmVzJE5hbWU8LXJlcy5hbm5vdFtyb3duYW1lcyhyZXMpLCAiTmFtZSJdCmhlYWQocmVzKQojR1JBTUQxQiBpcyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaW4gR1JBTUQxQiBncm91cHMgYW5kIGlzIGRvd25yZWd1bGF0ZWQgaW4gdGhlIGxvdyBleHByZXNzaW9uIGdyb3VwIGNvbXBhcmVkIHRvIHRoZSBoaWdoLWV4cHJlc3Npb24gZ3JvdXAgc28gYXRsZWFzdCB0aGlzIG1ha2VzIHNlbnNlLgpgYGAKI1BlcmZvcm0gbGZjIHNocmlua2FnZSBmb3IgR1NFQQpgYGB7cn0KbGlicmFyeSgiYXBlZ2xtIikKY29lZk5hbWUgPSByZXN1bHRzTmFtZXMoZGRzMSkKcmVzV2l0aExmY1Nocmlua2FnZSA9IGRhdGEuZnJhbWUobGZjU2hyaW5rKGRkczEsIGNvZWYgPSB0YWlsKGNvZWZOYW1lLCBuPTEpLCB0eXBlPSJhcGVnbG0iKSkKI0FkZCBzdWZmaXggdG8gY29sbmFtZXMgZm9yIGVhc3kgaWRlbnRpZmljYXRpb24gbGF0ZXIgb24uCmNvbG5hbWVzKHJlc1dpdGhMZmNTaHJpbmthZ2UpID0gcGFzdGUoY29sbmFtZXMocmVzV2l0aExmY1Nocmlua2FnZSksImxmY1Nocmlua0FwcGxpZWQiLCBzZXAgPSAiXyIpCiNBZGQgRU5TRU1CTCBJRCBhcyBhIGNvbHVtbiBpbiB0aGUgZGF0YSBmcmFtZSB0byBlYXNlIG1lcmdpbmcgbGF0ZXIKcmVzV2l0aExmY1Nocmlua2FnZSRpZCA9IHJvd25hbWVzKHJlc1dpdGhMZmNTaHJpbmthZ2UpCmBgYAoKYGBge3J9CiNNZXJnZSB0aGVzZSB3aXRoIERFIG9yaWdpbmFsIERFIHJlc3VsdHMKcmVzJGlkPC1yb3duYW1lcyhyZXMpCmRlUmVzdWx0c1VwZGF0ZWQgPSBtZXJnZSh4ID0gcmVzLCB5ID0gZGF0YS5mcmFtZShyZXNXaXRoTGZjU2hyaW5rYWdlKSwgYnkgPSAiaWQiKQpkZVJlc3VsdHNVcGRhdGVkID0gZGVSZXN1bHRzVXBkYXRlZFtvcmRlcihkZVJlc3VsdHNVcGRhdGVkJHBhZGopLF0KI1NhdmUgdGhlIHJlc3VsdHMKd3JpdGUudGFibGUoZGVSZXN1bHRzVXBkYXRlZFtkZVJlc3VsdHNVcGRhdGVkJHBhZGpfbGZjU2hyaW5rQXBwbGllZDwwLjEsIF0sIGZpbGUgPSAiLi4vcmVzdWx0cy9kZV9nc2VhL2RlV2l0aExmY1Nrcmlua2FnZUFwcGxpZWRfR1JBTUQxQl9hbGwudHN2IiwgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UsIHNlcCA9ICdcdCcpCiNTYXZlIHRoZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgKHBhZGo8MC4xKQp3cml0ZS50YWJsZShkZVJlc3VsdHNVcGRhdGVkW2RlUmVzdWx0c1VwZGF0ZWQkcGFkal9sZmNTaHJpbmtBcHBsaWVkPDAuMSwgXSwgZmlsZSA9ICIuLi9yZXN1bHRzL2RlX2dzZWEvZGVXaXRoTGZjU2tyaW5rYWdlQXBwbGllZF9HUkFNRDFCX3BhZGpfMF8xLnRzdiIsIGNvbC5uYW1lcyA9IFRSVUUsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFLCBzZXAgPSAnXHQnKQpgYGAKCmBgYHtyfQpoZWFkKGRlUmVzdWx0c1VwZGF0ZWQpCmBgYAoKI0NyZWF0ZSBhIHJhbmtlZCBsaXN0IGZvciBHU0VBCmBgYHtyfQojQ3JlYXRlIHJhbmtlZCBsaXN0IGZvciBydW5uaW5nIFByZS1yYW5rZWQgR1NFQSB0b29sCnJhbmtlZExpc3QgPSBuYS5vbWl0KGRhdGEuZnJhbWUoZGVSZXN1bHRzVXBkYXRlZCROYW1lLCBkZVJlc3VsdHNVcGRhdGVkJGxvZzJGb2xkQ2hhbmdlX2xmY1Nocmlua0FwcGxpZWQpKQpyYW5rZWRMaXN0ID0gcmFua2VkTGlzdFt3aXRoKHJhbmtlZExpc3QsIG9yZGVyKGRlUmVzdWx0c1VwZGF0ZWQubG9nMkZvbGRDaGFuZ2VfbGZjU2hyaW5rQXBwbGllZCkpLCBdCiNTYXZlIHRoZSByYW5rZWQgbGlzdCB0byBhIG5ldyBmaWxlIGZvciBHU0VBLgp3cml0ZS50YWJsZShyYW5rZWRMaXN0LCBmaWxlID0gIi4uL3Jlc3VsdHMvZGVfZ3NlYS9yYW5rZWRMaXN0X0dSQU1EMUIucm5rIiwgY29sLm5hbWVzID0gRkFMU0UsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFLCBzZXAgPSAnXHQnKQpgYGAKCiNQbG90IHN1cnZpdmFsIGN1cnZlcyBiZXR3ZWVuIHRoZSBjb250aW51b3VzIHNpZ25hdHVyZSBhbmQgdGhlIHR3byBncm91cHMKIyMgRmlyc3QgYWRkIG5lY2Vzc3NhcnkgY29sdW1ucwpgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoInN1cnZpdmFsIikpCiMgc3Vydml2YWwgb3V0Y29tZSAxOiAiZCIgZm9yIGRlYXRoIGFuZCAibHRyYyIgZm9yIGxlZnQgdHJ1bmNhdGVkIGFuZCByaWdodCBjZW5zb3JlZApjbGluaWNhbERhdGEkc3Vydml2YWxfZF9sdHJjID0gU3Vydih0aW1lICA9IGFzLm51bWVyaWMoZGlmZnRpbWUoY2xpbmljYWxEYXRhJERPRSwgY2xpbmljYWxEYXRhJEREaWFnKSkvMzY1LjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lMiA9IGFzLm51bWVyaWMoZGlmZnRpbWUoY2xpbmljYWxEYXRhJERPQywgY2xpbmljYWxEYXRhJEREaWFnKSkvMzY1LjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldmVudCA9IGlmZWxzZShjbGluaWNhbERhdGEkRGVhZCA9PSBGQUxTRSwgMCwgMSkpCgojIHN1cnZpdmFsIG91dGNvbWUgMjogInJkIiBmb3IgcmVsYXBzZSBvciBkZWF0aCwgImx0cmMiIGZvciBsZWZ0IHRydW5jYXRlZCBhbmQgcmlnaHQgY2Vuc29yZWQKY2xpbmljYWxEYXRhJHN1cnZpdmFsX3JkX2x0cmMgPSBTdXJ2KHRpbWUgID0gYXMubnVtZXJpYyhkaWZmdGltZShjbGluaWNhbERhdGEkRE9FLCBjbGluaWNhbERhdGEkRERpYWcpKS8zNjUuMjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lMiA9IGFzLm51bWVyaWMoZGlmZnRpbWUoYXBwbHkoY2xpbmljYWxEYXRhWywgYygiRE9DIiwgIkREaXN0TWV0cyIpXSwxLG1pbixuYS5ybT1UUlVFKSwgY2xpbmljYWxEYXRhJEREaWFnKSkvMzY1LjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXZlbnQgPSBpZmVsc2UoKGNsaW5pY2FsRGF0YSREZWFkID09IEZBTFNFICYgY2xpbmljYWxEYXRhJEV2ZW50TWV0ID09ICJObyIpLCAwLCAxKSkKCmlmKHRpc3N1ZT09IlNraW4iKXsKICBqdF9uc19zbV9zY29yZXMgPSByZWFkLnRhYmxlKCIuLi9KVF9OU19TTV9Db21iaW5lZC54bHMgLSBKVF9OU19TTS50c3YiLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSBUUlVFLCBxdW90ZSA9ICIiKQogIGNvbWJpbmVkU2NvcmUgPC0gZGF0YS5mcmFtZSgiU2Nhbm5lZF9maWxlLm1lIj0ganRfbnNfc21fc2NvcmVzJEpUX1NjYW5uZWRfZmlsZS5tZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSLlNlcV9zYW1wbGVJRCI9IGp0X25zX3NtX3Njb3JlcyRKVF9SLlNlcV9zYW1wbGVJRCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDbGFya1Njb3JlIj1pZmVsc2UoanRfbnNfc21fc2NvcmVzJEpUX0NsYXJrX3Njb3JlPT1qdF9uc19zbV9zY29yZXMkTlNfQ2xhcmtfc2NvcmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAganRfbnNfc21fc2NvcmVzJEpUX0NsYXJrX3Njb3JlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBqdF9uc19zbV9zY29yZXMkU01fQ2xhcmtfc2NvcmUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiTUlBU2NvcmUiPSBpZmVsc2UoanRfbnNfc21fc2NvcmVzJEpUX1RJTF9ncmFkZT09anRfbnNfc21fc2NvcmVzJE5TX1RJTC5HUkFERSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGp0X25zX3NtX3Njb3JlcyRKVF9USUxfZ3JhZGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAganRfbnNfc21fc2NvcmVzJFNNX1RJTF9ncmFkZSkpCiAgI1JlbW92ZSBkdXBsaWNhdGVkIGVudHJpZXMKICBjb21iaW5lZFNjb3JlPC1jb21iaW5lZFNjb3JlWyFkdXBsaWNhdGVkKGNvbWJpbmVkU2NvcmUkUi5TZXFfc2FtcGxlSUQpLCBdCiAgcm93bmFtZXMoY29tYmluZWRTY29yZSk8LWNvbWJpbmVkU2NvcmUkUi5TZXFfc2FtcGxlSUQgI3JlbmFtZSByb3duYW1lcyB3aXRoIHNhbXBsZSBpZHMKICBjb21iaW5lZFNjb3JlJENsYXJrU2NvcmUgPC0gYXMuZmFjdG9yKGNvbWJpbmVkU2NvcmUkQ2xhcmtTY29yZSkKICBsZXZlbHMoY29tYmluZWRTY29yZSRDbGFya1Njb3JlKSA8LSBjKCJhYnNlbnQiLCAibm9uYnJpc2siLCAiYnJpc2siKQogIGNsaW5pY2FsRGF0YTwtbWVyZ2UoY2xpbmljYWxEYXRhLCBjb21iaW5lZFNjb3JlLCBieS54PSJSTkEuU2VxLlNhbXBsZSIsIGJ5Lnk9IlIuU2VxX3NhbXBsZUlEIikKICByb3duYW1lcyhjbGluaWNhbERhdGEpPC1jbGluaWNhbERhdGEkUk5BLlNlcS5TYW1wbGUKfQpgYGAKCiMjIHRoZW4gY2FsY3VsYXRlIHRoZSB2YWx1ZXMKYGBge3J9Cm9zPC1kYXRhLmZyYW1lKCkKcGZzPC1kYXRhLmZyYW1lKCkKdGVtcDwtZGF0YS5mcmFtZSgpCgpjb21iaW5hdGlvbnM8LWMoIlNpZ25hdHVyZSIsICJTaWduYXR1cmVHcm91cE1lYW4iLCAiU2lnbmF0dXJlR3JvdXBNZWRpYW4iLCAiU2lnbmF0dXJlR3JvdXAwXzMzIiwgIlNpZ25hdHVyZUdyb3VwMF82NiIsICJTaWduYXR1cmVHcm91cERlbnNpdHlJbnRlcnNlY3Rpb24iKQoKZm9yIChjb21iaW5hdGlvbiBpbiBjb21iaW5hdGlvbnMpIHsKICBpZiAodGlzc3VlPT0iU2tpbiIpIHsKICAgIG9zX2Zvcm11bGE8LXBhc3RlMCgic3Vydml2YWxfZF9sdHJjfiIsY29tYmluYXRpb24sIitTZXgrQWdlK2FzLm51bWVyaWMoTmNsYXNzKSthcy5jaGFyYWN0ZXIoU3RhZ2UpK0VDT0crdHJlYXRtZW50K0NsYXJrU2NvcmUiKQogICAgcGZzX2Zvcm11bGE8LXBhc3RlMCgic3Vydml2YWxfcmRfbHRyY34iLGNvbWJpbmF0aW9uLCIrU2V4K0FnZSthcy5udW1lcmljKE5jbGFzcykrYXMuY2hhcmFjdGVyKFN0YWdlKStFQ09HK3RyZWF0bWVudCtDbGFya1Njb3JlIikKICB9IGVsc2V7CiAgICBvc19mb3JtdWxhPC1wYXN0ZTAoInN1cnZpdmFsX2RfbHRyY34iLGNvbWJpbmF0aW9uLCIrU2V4K0FnZSthcy5udW1lcmljKE5jbGFzcykrYXMuY2hhcmFjdGVyKFN0YWdlKStFQ09HK3RyZWF0bWVudCIpCiAgICBwZnNfZm9ybXVsYTwtcGFzdGUwKCJzdXJ2aXZhbF9yZF9sdHJjfiIsY29tYmluYXRpb24sIitTZXgrQWdlK2FzLm51bWVyaWMoTmNsYXNzKSthcy5jaGFyYWN0ZXIoU3RhZ2UpK0VDT0crdHJlYXRtZW50IikKICB9CiAgCiAgZml0ID0gY29lZihzdW1tYXJ5KGNveHBoKGFzLmZvcm11bGEob3NfZm9ybXVsYSksIGRhdGE9Y2xpbmljYWxEYXRhKSkpCiAgbWlkICA9IGZpdFsxLGMoImV4cChjb2VmKSIpXQogIGxvdyAgPSBleHAoZml0WzEsYygiY29lZiIpXS0KICAgICAgICAgICAgICBxbm9ybSguOTc1KSpmaXRbMSxjKCJzZShjb2VmKSIpXSkgICAgICAgICAgICAgICAgICAgCiAgaGlnaCA9IGV4cChmaXRbMSxjKCJjb2VmIildKwogICAgICAgICAgICAgIHFub3JtKC45NzUpKmZpdFsxLGMoInNlKGNvZWYpIildKSAgCiAgcHZhbCA9IGZpdFsxLGMoIlByKD58enwpIildCiAgdGVtcFsiU2lnbmF0dXJlIixjKCJTaWduYXR1cmUiLCAiSFIiLCJsb3ciLCJoaWdoIiwicHZhbCIpXSA9IGMocm93bmFtZXMoZml0KVsxXSxtaWQsbG93LGhpZ2gscHZhbCkKICBvczwtcmJpbmQob3MsIHRlbXApCiAgCiAgZml0ID0gY29lZihzdW1tYXJ5KGNveHBoKGFzLmZvcm11bGEocGZzX2Zvcm11bGEpLCBkYXRhPWNsaW5pY2FsRGF0YSkpKQogIG1pZCAgPSBmaXRbMSxjKCJleHAoY29lZikiKV0KICBsb3cgID0gZXhwKGZpdFsxLGMoImNvZWYiKV0tCiAgICAgICAgICAgICAgcW5vcm0oLjk3NSkqZml0WzEsYygic2UoY29lZikiKV0pICAgICAgICAgICAgICAgICAgIAogIGhpZ2ggPSBleHAoZml0WzEsYygiY29lZiIpXSsKICAgICAgICAgICAgICBxbm9ybSguOTc1KSpmaXRbMSxjKCJzZShjb2VmKSIpXSkgIAogIHB2YWwgPSBmaXRbMSxjKCJQcig+fHp8KSIpXQogIHRlbXBbIlNpZ25hdHVyZSIsYygiU2lnbmF0dXJlIiwgIkhSIiwibG93IiwiaGlnaCIsInB2YWwiKV0gPSBjKHJvd25hbWVzKGZpdClbMV0sbWlkLGxvdyxoaWdoLHB2YWwpCiAgcGZzPC1yYmluZChwZnMsIHRlbXApCn0KCndyaXRlLnRhYmxlKG9zLCBwYXN0ZSgiLi4vcmVzdWx0cy9zdXJ2aXZhbC9BVkFTVC1NXyIsdGlzc3VlLCJfb3MudHN2Iiwgc2VwPSIiKSwgc2VwPSJcdCIsIGNvbC5uYW1lcyA9IFQsIHJvdy5uYW1lcyA9IEYpCndyaXRlLnRhYmxlKHBmcywgcGFzdGUoIi4uL3Jlc3VsdHMvc3Vydml2YWwvQVZBU1QtTV8iLHRpc3N1ZSwiX3Bmcy50c3YiLCBzZXA9IiIpLCBzZXA9Ilx0IiwgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gRikKYGBgCg==